<!-- SigmaGraph.vue -->
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';

import Graph from 'graphology';
import { circular } from 'graphology-layout';
import FA2, { ForceAtlas2Layout } from 'graphology-layout-forceatlas2/worker';
import Sigma from 'sigma';

// Props 类型
interface NodeData {
  data: {
    clusters?: string;
    description?: string;
    entity_type?: string;
    id: string;
    label: string;
    source_id?: string;
  };
}
interface EdgeData {
  data: {
    description?: string;
    order?: number;
    source: string;
    source_id?: string;
    target: string;
    weight?: number;
  };
}
interface Elements {
  nodes: NodeData[];
  edges: EdgeData[];
}

const props = defineProps<{ elements: Elements }>();

const container = ref<HTMLDivElement | null>(null);
let renderer: null | Sigma = null;
let graph: Graph | null = null;
let layout: FA2 | null = null;

// 彩色节点
function getRandomColor() {
  const palette = ['#60A5FA', '#34D399', '#FBBF24', '#F87171', '#A78BFA'];
  return palette[Math.floor(Math.random() * palette.length)];
}

function buildGraph(elements: Elements) {
  const g = new Graph();

  elements.nodes.forEach(({ data }) => {
    g.addNode(data.id, {
      label: data.label,
      size: 6,
      color: getRandomColor(),
    });
  });

  elements.edges.forEach(({ data }) => {
    if (g.hasNode(data.source) && g.hasNode(data.target)) {
      g.addEdge(data.source, data.target, {
        size: 1,
        color: 'rgba(150,150,150,0.3)',
      });
    }
  });

  return g;
}

function renderGraph(g: Graph) {
  if (!container.value) return;

  if (renderer) renderer.kill();
  if (layout) layout.kill();

  // 初始环形布局，避免重叠
  circular.assign(g);

  layout = new FA2(g, {
    settings: {
      scalingRatio: 400, // 原来是 50，拉得太近
      gravity: 0.005, // 原来是 0.05，太大导致中心聚合
      strongGravityMode: true, // 保证孤立的簇能拉开
      linLogMode: true, // 更适合图聚类分布
      barnesHutOptimize: true,
      adjustSizes: true,
      slowDown: 2,
    },
  });

  layout.start();

  renderer = new Sigma(g, container.value);
}
function runForceAtlas2(g: Graph) {
  const layout = new ForceAtlas2Layout(g, {
    settings: {
      gravity: 0.005,
      scalingRatio: 500,
      strongGravityMode: true,
      adjustSizes: true,
      slowDown: 2,
      barnesHutOptimize: true,
    },
  });

  layout.start();

  // 停 3 秒，强制停止后渲染图
  setTimeout(() => {
    layout.stop();
    renderGraph(g);
  }, 3000); // 可改成 5000 等时间拉得更远
}
onMounted(() => {
  graph = buildGraph(props.elements);
  runForceAtlas2(graph); // 用 Worker 异步跑布局
});
watch(
  () => props.elements,
  (newVal) => {
    graph = buildGraph(newVal);
    renderGraph(graph!);
  },
  { deep: true },
);
</script>

<template>
  <div ref="container" class="h-[90vh] w-full rounded-md border"></div>
</template>

<style scoped></style>
